﻿using System;
using System.Reflection;

namespace XDFLib.Extensions.Reflections
{
    public static class ReflectionExtensions
    {
        public static object GetFieldValue(this object obj, string fieldName)
        {
            var fieldInfo = obj.GetType().GetField(fieldName);
            if (fieldInfo != null)
            {
                var objRef = __makeref(obj);
                return fieldInfo.GetValueDirect(objRef);
            }
            return null;
        }

        public static (bool, T) GetFieldValue<T>(this object obj, string fieldName)
        {
            var fieldInfo = obj.GetType().GetField(fieldName);
            if (fieldInfo != null)
            {
                var objRef = __makeref(obj);
                if (typeof(T).IsAssignableFrom(fieldInfo.FieldType))
                {
                    return (true, (T)fieldInfo.GetValueDirect(objRef));
                }
            }
            return (false, default(T));
        }

        public static bool SetFieldValue(this object obj, string fieldName, object newValue)
        {
            if (obj != null)
            {
                var fieldInfo = obj.GetType().GetField(fieldName);
                if (fieldInfo != null)
                {
                    if (fieldInfo.FieldType.IsAssignableFrom(newValue.GetType()))
                    {
                        fieldInfo.SetValue(obj, newValue);
                        return true;
                    }
                }
            }
            return false;
        }

        public static bool SetFieldValue<T>(this object obj, string fieldName, T newValue)
        {
            if (obj != null)
            {
                var fieldInfo = obj.GetType().GetField(fieldName);
                if (fieldInfo != null)
                {
                    if (fieldInfo.FieldType.IsAssignableFrom(newValue.GetType()))
                    {
                        fieldInfo.SetValue(obj, newValue);
                        return true;
                    }
                }
            }
            return false;
        }

        public static object GetPropertyValue(this object obj, string propertyName)
        {
            var propInfo = obj.GetType().GetProperty(propertyName);
            if (propInfo != null)
            {
                return propInfo.GetValue(obj);
            }
            return null;
        }

        public static (bool, T) GetPropertyValue<T>(this object obj, string propertyName)
        {
            var propInfo = obj.GetType().GetProperty(propertyName);
            if (propInfo != null)
            {
                if (typeof(T).IsAssignableFrom(propInfo.PropertyType))
                {
                    return (true, (T)propInfo.GetValue(obj));

                }
            }
            return (false, default(T));
        }

        public static bool SetPropertyValue(this object obj, string propertyName, object newProp)
        {
            var propInfo = obj.GetType().GetProperty(propertyName);
            if (propInfo != null)
            {
                if (propInfo.PropertyType.IsAssignableFrom(newProp.GetType()))
                {
                    propInfo.SetValue(obj, newProp);
                    return true;
                }
            }
            return false;
        }

        public static bool SetPropertyValue<T>(this object obj, string propertyName, T newProp)
        {
            var propInfo = obj.GetType().GetProperty(propertyName);
            if (propInfo != null)
            {
                if (propInfo.PropertyType.IsAssignableFrom(newProp.GetType()))
                {
                    propInfo.SetValue(obj, newProp);
                    return true;
                }
            }
            return false;
        }

        public static TInfo GetAttributeInfo<Tobj, Tatt, TInfo>(Tobj e, Func<Tatt, TInfo> infoGetter) where Tatt : Attribute
        {
            var type = e.GetType();
            var memInfo = type.GetMember(e.ToString());
            var attribute = memInfo[0].GetCustomAttribute(typeof(Tatt), false) as Tatt;
            if (attribute != null)
            {
                return infoGetter(attribute);
            }
            return default;
        }

    }
}
